using System;
using Microsoft.Extensions.Logging;

namespace ObjectSerializers.Abstractions
{
    /// <summary>
    /// 基础抽象的序列化上下文
    /// </summary>
    public abstract class BasicSerializerContext : ISerializerContext
    {
        /// <summary>
        /// 日志
        /// </summary>
        protected ILogger<BasicSerializerContext> Logger { get; }

        /// <summary>
        /// 是否为调试
        /// </summary>
        private bool IsDebug => this.Logger.IsEnabled(LogLevel.Debug);

        public BasicSerializerContext(ILogger<BasicSerializerContext> logger)
        {
            Logger = logger;
        }

        public ReadOnlyMemory<byte> Serialize<T>(T value)
        {
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }

            var nowTicks = this.IsDebug ? DateTime.Now.Ticks : long.MinValue;

            try
            {
                var buffer = this.OnSerialize(value);

                this.Logger.LogDebug("{Name} Serialize Time Costs {Ticks}ns", typeof(T).Name,
                    DateTime.Now.Ticks - nowTicks);
                return buffer;
            }
            catch (Exception ex)
            {
                this.Logger.LogError(ex, $"{nameof(Exception)} in Serializing");

                throw new SerializeException($"{typeof(T).Name}", value, this.GetType().Name);
            }
        }

        public T Deserialize<T>(ReadOnlyMemory<byte> buffer)
        {
            if (buffer.IsEmpty)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            var nowTicks = this.IsDebug ? DateTime.Now.Ticks : long.MinValue;
            try
            {
                var value = this.OnDeserialize<T>(buffer);

                this.Logger.LogDebug("{Name} Deserialize Time Costs {Ticks}ns", typeof(T).Name,
                    DateTime.Now.Ticks - nowTicks);
                return value;
            }
            catch (Exception ex)
            {
                this.Logger.LogError(ex, $"{nameof(Exception)} in Deserializing");

                throw new DeserializeException($"{typeof(T).Name}", buffer, this.GetType().Name);
            }
        }

        protected abstract ReadOnlyMemory<byte> OnSerialize<T>(T value);
        protected abstract T OnDeserialize<T>(ReadOnlyMemory<byte> buffer);
    }
}